package com.teaphy.teacompiler.processor;

import com.google.auto.service.AutoService;
import com.teaphy.teaannotation.annotation.Factory;
import com.teaphy.teacompiler.generate.FactoryGroupedClasses;
import com.teaphy.teacompiler.module.FactoryAnnotatedClass;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;

@AutoService(Processor.class)
public class FactoryProcessor extends AbstractProcessor {

	private Types typeUtils;
	private Messager messager;
	private Filer filer;
	private Elements elementUtils;

	private Map<String, FactoryGroupedClasses> factoryClasses = new LinkedHashMap<>();

	/**
	 * 被注解处理工具调用，并输入ProcessingEnviroment参数。
	 *
	 * @param processingEnv 提供很多有用的工具类Elements, Types和Filer。
	 */
	@Override
	public synchronized void init(ProcessingEnvironment processingEnv) {
		super.init(processingEnv);
		typeUtils = processingEnv.getTypeUtils();
		messager = processingEnv.getMessager();
		filer = processingEnv.getFiler();
		elementUtils = processingEnv.getElementUtils();
	}

	/**
	 * 注解处理器是注册给哪个注解的。
	 */
	@Override
	public Set<String> getSupportedAnnotationTypes() {

		Set<String> annotations = new LinkedHashSet<>();
		annotations.add(Factory.class.getCanonicalName());
		return annotations;
	}

	/**
	 * 用来指定你使用的Java版本。
	 */
	@Override
	public SourceVersion getSupportedSourceVersion() {
		return SourceVersion.latestSupported();
	}

	/**
	 * 注解处理函数，用来扫描、评估和处理注解的代码，以及生成Java文件。
	 */
	@Override
	public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

		// 扫描被Factory注解的元素
		Set<? extends Element> elements = roundEnv
			.getElementsAnnotatedWith(Factory.class);
		try {
			for (Element element : elements) {
				// 1. 检查被注解为@Factory的元素是否是一个类
				if (element.getKind() != ElementKind.CLASS) {
					error(element, "Only classes can be annotated with @%s",
						Factory.class.getSimpleName());
					return true;
				}

				TypeElement typeElement = (TypeElement) element;

				FactoryAnnotatedClass factoryAnnotatedClass = new FactoryAnnotatedClass(typeElement);

				info(factoryAnnotatedClass.toString());

				// 2. 判断FactoryAnnotatedClass的有效性
				if (!isValidClass(factoryAnnotatedClass)) {
					return true; // 已经打印了错误信息，退出处理过程
				}


				FactoryGroupedClasses factoryClass = factoryClasses
					.get(factoryAnnotatedClass.getQualifiedSuperClassName());
				if (factoryClass == null) {
					String qualifiedGroupName = factoryAnnotatedClass.getQualifiedSuperClassName();
					factoryClass = new FactoryGroupedClasses(qualifiedGroupName);
					factoryClasses.put(qualifiedGroupName, factoryClass);
				}

				factoryClass.add(factoryAnnotatedClass);
			}



		} catch (IllegalAccessException e) {
			e.printStackTrace();
			error(e.getMessage());
		}

		// 生成代码
		try {
			for (FactoryGroupedClasses factoryClass : factoryClasses.values()) {
				factoryClass.generateCode(elementUtils, filer);
			}
			factoryClasses.clear();
		} catch (IOException e) {
			e.printStackTrace();
			error(e.getMessage());
		}

		return true;
	}

	/**
	 * 判断是否为合法的Class
	 */
	private boolean isValidClass(FactoryAnnotatedClass factoryAnnotatedClass) {

		TypeElement typeElement = factoryAnnotatedClass.getAnnotatedElement();

		// 1.  检查是否为 public
		if (!typeElement.getModifiers().contains(Modifier.PUBLIC)) {

			error(typeElement,
				String.format("%s isn't public",
					factoryAnnotatedClass.getQualifiedSuperClassName()));

			return false;
		}

		// 2.  检查是否是一个抽象类
		if (typeElement.getModifiers().contains(Modifier.ABSTRACT)) {

			error(typeElement,
				String.format("%s is abstract. Your can't annotate the abstract class with %s",
					typeElement.getSimpleName(),
					Factory.class.getSimpleName()));

			return false;
		}

		// 3. 检查继承关系：必须是@Factory.type()指定类型的子类
		TypeElement supperElement = elementUtils
			.getTypeElement(factoryAnnotatedClass.getQualifiedSuperClassName());
		info("supperElement：%s", supperElement.toString());
		info("typeElement：%s", typeElement.toString());
		// 判断Factory.type的类型
		// 1. Factory.type的类型为interface
		if (supperElement.getKind() == ElementKind.INTERFACE) { // 是接口
			// 判断typeElement是否直接继承于 Factory.type 指定的接口
			if(!typeElement.getInterfaces().contains(supperElement.asType())) {
				// 如果typeElement未直接继承于 Factory.type 指定的接口，判断其父类是否继承于该接口
				// 检查父类
				TypeElement currentClass = typeElement;
				while (true) {
					TypeMirror supperClass = currentClass.getSuperclass();
					if (supperClass.getKind() == TypeKind.NONE) {
						// 到达了基本类型(java.lang.Object), 所以退出
						error(typeElement, "The class %s annotated with @%s must inherit from %s",
							typeElement.getQualifiedName().toString(), Factory.class.getSimpleName(),
							factoryAnnotatedClass.getQualifiedSuperClassName());
						return false;
					}

					if (supperClass.getKind() != TypeKind.DECLARED) {
						error(typeElement, "The supper class %s is not class or interface",
							supperClass.toString());
						return false;
					}

					DeclaredType supperType = (DeclaredType) supperClass;
					TypeElement se = (TypeElement) supperType.asElement();

					if (se.getInterfaces().contains(supperElement.asType())) {
						break;
					}

					currentClass = (TypeElement) typeUtils.asElement(supperClass);
				}

			}
		} else { // 2. Factory.type的类型为class

			TypeElement currentClass = typeElement;

			while (true) {
				TypeMirror superclass = currentClass.getSuperclass();

				if (superclass.getKind() == TypeKind.NONE) {
					// 到达了基本类型(java.lang.Object), 所以退出
					error(typeElement, "The class %s annotated with @%s must inherit from %s",
						typeElement.getQualifiedName().toString(), Factory.class.getSimpleName(),
						factoryAnnotatedClass.getQualifiedSuperClassName());
					return false;
				}

				if (superclass.getKind() != TypeKind.DECLARED) {
					error(typeElement, "The supper class %s is not class or interface",
						superclass.toString());
					return false;
				}

				DeclaredType declaredType = (DeclaredType) superclass;

				if (declaredType.asElement().equals(supperElement)) {
					// 找到了要求的父类
					break;
				}

				currentClass = (TypeElement) typeUtils.asElement(superclass);
			}
		}

		// 检查是否提供了默认公开构造函数
		for (Element enclosed : typeElement.getEnclosedElements()) {
			if (enclosed.getKind() == ElementKind.CONSTRUCTOR) {
				ExecutableElement constructorElement = (ExecutableElement) enclosed;
				if (constructorElement.getParameters().size() == 0 && constructorElement
					.getModifiers()
					.contains(Modifier.PUBLIC)) {
					// 找到了默认构造函数
					return true;
				}
			}
		}

		// 没有找到默认构造函数
		error(typeElement, "The class %s must provide an public empty default constructor",
			typeElement.getQualifiedName().toString());

		return false;
	}


	private void error(Element e, String msg, Object... args) {
		messager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args), e);
	}

	private void error(String msg, Object... args) {
		messager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args));
	}

	private void info(String msg, Object... args) {
		messager.printMessage(Diagnostic.Kind.NOTE, String.format(msg, args));
	}
}
